#ifndef __VEC_H
#define __VEC_H
#include <memory>

// #include <vector>
// #define Vec std::vector

//TODO
template <class T>
class Vec {
public:
    typedef T* iterator;
    typedef const T* const_iterator;
    typedef size_t size_type;
    typedef T value_type;
    typedef ptrdiff_t difference_type;
    typedef T& reference;
    Vec(){create();}
    explicit Vec(std::size_t n, const T& val=T()){
        create(n,val);
    }
    Vec(const Vec& v){
        create(v.begin(), v.end());
    }

    size_type size() const;
    T& operator[](size_type i);
    const T& operator[](size_type i) const;
    Vec<T> & operator=(const Vec& v);

    iterator begin(){return data;}
    const_iterator begin() const{return data;}

    iterator end(){return avail;}
    const_iterator end() const{return avail;}
    ~Vec(){uncreate();} 
    void push_back(const T& val){
        if(avail == limit){
            grow();
        }
        unchecked_append(val);
    };
    void clear();
private:

    iterator data;
    iterator avail;
    iterator limit;
    void create();
    void create(size_type n, const T& val);
    void create(const_iterator a, const_iterator b);
    void uncreate();
    void grow();
    void unchecked_append(const T& val);
    std::allocator<T> alloc;

};


template <class T>
typename Vec<T>::size_type Vec<T>::size() const{
    return avail - data;
}

template <class T>
T& Vec<T>::operator[](size_type i){
    return data[i];
}
template <class T>
const T& Vec<T>::operator[](size_type i) const{
    return data[i];
}

template <class T>
Vec<T>& Vec<T>::operator=(const Vec& v){
    if(&v != this){
        uncreate();
        create(v.begin(), v.end());
    }
    return*this;
}
template <class T>
void Vec<T>::create(){
    data = avail = limit = nullptr;
}

template <class T>
void Vec<T>::create(size_type n, const T& val){
    data = alloc.allocate(n,0);
    limit = avail = data + n;
    std::uninitialized_fill(data, limit, val);
}
template <class T>
void Vec<T>::create(const_iterator a, const_iterator b){
    data = alloc.allocate(b - a, 0);
    limit = avail = std::uninitialized_copy(a, b, data);
}


template <class T>
void Vec<T>::uncreate(){
    if (data){
        iterator it = avail;
        while (it != data){
            alloc.destroy(--it);
        }
        alloc.deallocate(data, limit - data);
    }
    data=limit=avail=0;
}

template <class T>
void Vec<T>::grow(){
    size_type new_size = 1;
    if (limit != data){
        new_size = 2 * (limit - data);
    }
    iterator new_data = alloc.allocate(new_size, 0);
    iterator new_avail = std::uninitialized_copy(data, avail, new_data);
    uncreate();
    data = new_data;
    avail = new_avail;
    limit= data + new_size;
}

template <class T>
void Vec<T>::unchecked_append(const T& val){
    alloc.construct(avail++, val);
}

template <class T>
void Vec<T>::clear(){
    uncreate();
    create();
}
#endif
